/*
 * Copyright (c) 2024-2025 iSOFT INFRASTRUCTURE SOFTWARE CO., LTD.
 * easyAda is licensed under Mulan PubL v2.
 * You can use this software according to the terms and conditions of the Mulan PubL v2.
 * You may obtain a copy of Mulan PubL v2 at:
 *          http://license.coscl.org.cn/MulanPubL-2.0
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PubL v2 for more details.
 */

#include <plat/config.h>
#include <stdlib/assert.h>
#include <stdlib/stdint.h>
#include <tools/macros.h>
#include <core/addrspace.h>
#include <core/allocate.h>
#include <core/endpoint.h>
#include <core/notification.h>
#include <core/schedule.h>
#include <core/task.h>
#include <caps/signal.h>
#include <caps/mutex.h>
#include <caps/cond.h>
#include <caps/addrspace.h>
#include <caps/armsmc.h>
#include <caps/capability.h>
#include <caps/cnode.h>
#include <caps/endpoint.h>
#include <caps/irq.h>
#include <caps/notification.h>
#include <caps/ram.h>
#include <caps/task.h>
#include <uapi/errors.h>
#include <uapi/initialcaps.h>
#include <uapi/ipcbuffer.h>
#include <uapi/ipcops.h>

capability_t ALIGNED(1 << CAP_TYPE_BITS) initial_cspace[1 << CONFIG_INITIAL_CNODE_RADIX];

const unsigned int capobj_size[] = {
    [CAP_EMPTY]       = 0,
    [CAP_CNODE]       = sizeof(capability_t) * (1UL << CONFIG_THREAD_CNODE_RADIX),
    [CAP_NTFN]        = sizeof(notification_t),
    [CAP_EP]          = sizeof(endpoint_t),
    [CAP_IRQ_CONTROL] = 0,
    [CAP_IRQ_HANDLER] = 0,
    [CAP_THREAD]      = sizeof(tcb_t),
    [CAP_RAM]         = 0,
    [CAP_ADDRSPACE]   = sizeof(addrspace_t),
    [CAP_ARMSMC]      = 0,
    [CAP_MUTEX]       = sizeof(mutex_t),
    [CAP_COUNT]       = 0,
};

const cap_dispatcher_t capdispatchers[] = {
    [CAP_CNODE]       = cap_cnode_dispatcher,
    [CAP_IRQ_CONTROL] = cap_irqc_dispatcher,
    [CAP_IRQ_HANDLER] = cap_irqh_dispatcher,
    [CAP_EP]          = cap_ep_dispatcher,
    [CAP_NTFN]        = cap_ntfn_dispatcher,
    [CAP_RAM]         = cap_ram_dispatcher,
    [CAP_THREAD]      = cap_task_dispatcher,
    [CAP_ADDRSPACE]   = NULL,
    [CAP_MUTEX]       = cap_mutex_dispatcher,
    [CAP_COND]        = cap_cond_dispatcher,
    [CAP_SIGNAL]      = cap_signal_dispatcher,
#ifdef CONFIG_FORWARD_SMC
    [CAP_ARMSMC] = cap_armsmc_dispatcher,
#endif
    [CAP_COUNT] = NULL,
};

#define PERFORMANCE_COUNTER_INCREASE
#ifdef ENABLE_PERFORMANT_TEST
static inline unsigned long ts(void)
{
    unsigned long ret;
    __asm__ __volatile__("mrc p15, 0, %0, c9, c13, 0\n" : "=r"(ret) :);
    return ret;
}
#endif

void cap_dispatcher(unsigned long cptr, unsigned long tag, unsigned long m0, unsigned long m1, unsigned long m2,
                    unsigned long m3, unsigned long m4, unsigned long m5)
{
#ifdef ENABLE_PERFORMANT_TEST
    unsigned long        begin = ts();
    unsigned long        end;
    static unsigned long tscount = 0;
    static unsigned long tss     = 0;
    static unsigned long max     = 0;
#endif

    capability_t *cap = cnode_cptr_lookup(task_get_cnode(current()), cptr, sizeof(unsigned long) * 8);
    if (!cap) {
        return;
    }
    unsigned int type = cap_get_type(cap);

    assert(type < CAP_COUNT);

#ifdef DEBUG_DUMP_SVC
    dprintf("Caps Dispatcher: %x\n", cptr);
    dprintf("TAG info: len(%d bytes), op(%d), extra(0x%x).\n", msgtag_get_len(tag), msgtag_get_op(tag),
            msgtag_get_extra(tag));
    dprintf("Messages in IPCbuffer:\n");
    if (msgtag_get_len(tag) > 2) {
        int i;
        for (i = 2; i < msgtag_get_len(tag); i++) {
            dprintf("  [%d] = 0x%x\n", i, ipcbuffer_get_msgs(task_get_ipcbuffer(current()))[i]);
        }
    } else {
        dprintf("  None!\n");
    }
#endif

    if (capdispatchers[type] == NULL) {
        task_set_retcode(current(), ERR_TO_RETCODE(-ENOCAP));
    } else {
        capdispatchers[type](cap, tag, m0, m1, m2, m3, m4, m5);
    }

#ifdef ENABLE_PERFORMANT_TEST
    end = ts();
#ifdef PERFORMANCE_COUNTER_INCREASE
    {
        unsigned long tmp = end;
        end               = begin;
        begin             = tmp;
    }
#endif
    if (begin > end) {
        tscount++;
        tss += begin - end;

        if (begin - end >= 10000) {
            dprintf("Caps Dispatcher: %x\n", cptr);
            dprintf("TAG info: len(%d bytes), op(%d), extra(0x%x).\n", msgtag_get_len(tag), msgtag_get_op(tag),
                    msgtag_get_extra(tag));
            dprintf("Messages in IPCbuffer:\n");
            if (msgtag_get_len(tag) > 2) {
                int i;
                for (i = 2; i < msgtag_get_len(tag); i++) {
                    dprintf("  [%d] = 0x%x\n", i, ipcbuffer_get_msgs(task_get_ipcbuffer(current()))[i]);
                }
            } else {
                dprintf("  None!\n");
            }
        }

        if (begin - end > max) {
            max = begin - end;
        }

        if (tscount % 5 == 0) {
            dprintf("%s(): count = %d, total = %d, avr = %d, max = %d\n", __FUNCTION__, tscount, tss, tss / tscount,
                    max);
        }
    }
#endif
}

capability_t *initial_cspace_init(void *init, void *addrspace, void *faulthandler)
{
    capability_t *cspace;
    capability_t *cur;

    cspace = initial_cspace;
    assert(cspace != 0);

    cspace_audit_init(cspace + CAP_INDEX_AUDIT);

    cur = cspace + CAP_INDEX_FAULTHANDLER;
    cap_ep_init(cur, faulthandler, 0);

    cur = cspace + CAP_INDEX_THREAD;
    cap_task_init(cur, init);

    cur = cspace + CAP_INDEX_ADDRSPACE;
    cap_addrspace_init(cur, addrspace);

    cur = cspace + CAP_INDEX_IRQ_CONTROL;
    cap_irqc_init(cur);

    cur = cspace + CAP_INDEX_RAM_KERNEL;
    cap_ram_init(initial_cspace + CAP_INDEX_RAM_KERNEL, 0, CAP_RAM_KERNEL, 0);

    cur = cspace + CAP_INDEX_COUNT;
#ifdef CONFIG_FORWARD_SMC
    cap_armsmc_init(cur++);
#endif

    while (cur != (cspace + CONFIG_INITIAL_CNODE_COUNT)) {
        cap_make_empty(cur);
        cur++;
    }

    return cspace;
}

static capability_t *create_caps_for_region(const region_t *reg, capability_t *cur, unsigned int type)
{
    while (reg->size != 0UL) {
        assert(cap_is_empty(cur) && cur != (initial_cspace + CONFIG_INITIAL_CNODE_COUNT));
        cap_ram_init(cur, (unsigned long)reg, type, reg->directmap);

        cur++;
        reg++;
    }

    return cur;
}
